from PyQt5.QtWidgets import (
    QFrame, QHBoxLayout, QLabel, QPushButton, QApplication, QToolTip,
    QStylePainter, QStyleOptionButton,QStyle,
    QToolBar, QWidget, QAction, QVBoxLayout,
    QDockWidget, QMenu, QMainWindow, QListWidget, QLineEdit, QTabWidget
)

from PyQt5.QtGui import (
    QPixmap, QMouseEvent,QFontMetrics,QPaintEvent
)

from PyQt5.QtCore import (
    pyqtSlot,pyqtSignal, Qt, QEvent, QPoint, QCoreApplication,QSize,QRect,QObject
)

import functools

from enum import Enum

import src.QtExtrasUI_hli.resources.app_resources


s_autoHideDisabledStyle = """
QPushButton {
    border: 0px;
    width: 15px; height: 15px;
    image: url(:/Dock/Resources/pin_dockwidget_normal.png);
}
QPushButton:hover {
    image: url(:/Dock/Resources/pin_dockwidget_hover.png);
}
QPushButton:pressed:hover {
    image: url(:/Dock/Resources/pin_dockwidget_pressed.png);
}
"""

s_autoHideEnabledStyle = """
QPushButton {
    border: 0px;
    width: 15px; height: 15px;
    image: url(:/Dock/Resources/unpin_dockwidget_normal.png);
}
QPushButton:hover {
    image: url(:/Dock/Resources/unpin_dockwidget_hover.png);
}
QPushButton:pressed:hover {
    image: url(:/Dock/Resources/unpin_dockwidget_pressed.png);
}
"""

s_closeButtonStyle = """
QPushButton {
    border: 0px;
    width: 15px; height: 15px;
    image: url(:/Dock/Resources/close_dockwidget_normal.png);
}
QPushButton:hover {
    image: url(:/Dock/Resources/close_dockwidget_hover.png);
}
QPushButton:pressed:hover {
    image: url(:/Dock/Resources/close_dockwidget_pressed.png);
}
"""

s_menuButtonStyle = """
QPushButton {
    border: 0px;
    width: 15px; height: 15px;
    image: url(:/Dock/Resources/menu_dockwidget_normal.png);
}
QPushButton:hover {
    image: url(:/Dock/Resources/menu_dockwidget_hover.png);
}
QPushButton:pressed:hover {
    image: url(:/Dock/Resources/menu_dockwidget_pressed.png);
}
"""


def areaToOrientation(area):
   """
   areaToOrientation(area) -> Qt.Orientation
   param: area Qt.DockWidgetArea
   """
   if area == Qt.LeftDockWidgetArea or area == Qt.RightDockWidgetArea:
       return Qt.Vertical
   elif area == Qt.TopDockWidgetArea or area == Qt.BottomDockWidgetArea:
       return Qt.Horizontal
   else:
       return Qt.Orientation(0)

class QEDockWidgetTitle(QFrame):

    menuButton_pressed = pyqtSignal()
    autoHideButton_pressed = pyqtSignal()
    closeButton_pressed = pyqtSignal()
    def __init__(self):
        super().__init__(None)

        self.__m_LMPressed = False
        self.__m_autoHideEnabled = False
        self.__m_textLabel = QLabel()
        self.__m_menuButton = QPushButton()
        self.__m_autoHideButton = QPushButton()
        self.__m_closeButton = QPushButton()

        self.setObjectName("QEDockWidgetTitle")

        layout = QHBoxLayout()
        self.setLayout(layout)

        layout.setContentsMargins(3, 2, 3, 2)
        layout.setSpacing(1)

        layout.addWidget(self.__m_textLabel)
        layout.addStretch(1)

        self.__m_menuButton.setStyleSheet(s_menuButtonStyle)
        self.__m_menuButton.setToolTip("Menu")
        layout.addWidget(self.__m_menuButton)

        self.__m_autoHideButton.setStyleSheet(s_autoHideDisabledStyle)
        self.__m_autoHideButton.setToolTip("Auto Hide")
        self.__m_autoHideEnabled = True
        layout.addWidget(self.__m_autoHideButton)

        self.__m_closeButton.setStyleSheet(s_closeButtonStyle)
        self.__m_closeButton.setToolTip("Close")
        layout.addWidget(self.__m_closeButton)

        # self.menuButton_pressed = pyqtSignal()
        # self.autoHideButton_pressed = pyqtSignal()
        # self.closeButton_pressed = pyqtSignal()

        self.__m_menuButton.clicked.connect(self.menuButton_pressed)
        self.__m_autoHideButton.clicked.connect(self.autoHideButton_pressed)
        self.__m_closeButton.clicked.connect(self.closeButton_pressed)

    def getText(self):
        """
        Returns the title text.
        return: str
        """
        return self.__m_textLabel.text()
    
    def setText(self, text):
        """
        Sets the title text.
        param: text str
        """
        self.__m_textLabel.setText(text)

    def setFloating(self, state):
        """
        Sets the floating state of the title bar.
        param: state bool
        """
        self.__m_autoHideButton.setVisible(state)
    
    def setAutoHideEnadled(self, enabled):
        """
        Sets the auto hide state of the title bar.
        param: enabled bool
        """
        self.__m_autoHideEnabled = enabled
        if enabled:
            self.__m_autoHideButton.setStyleSheet(s_autoHideEnabledStyle)
        else:
            self.__m_autoHideButton.setStyleSheet(s_autoHideDisabledStyle)
            
    def menuPos(self) -> QPoint:
        """
        Returns the position of the menu button relative to the top-left corner of the title bar.
        return: QPoint
        """
        p = self.__m_menuButton.pos()
        p = QPoint(p.x(), self.__m_menuButton.height() + p.y())
        return QPoint(self.mapToGlobal(p))
    
    def mousePressEvent(self, event: QMouseEvent):
        """
        Reimplemented from QWidget::mousePressEvent().
        param: event QMouseEvent
        """
        if event.button() == Qt.LeftButton and self.__m_autoHideEnabled:
            self.__m_LMPressed = True
        
        super().mousePressEvent(event)

    def mouseReleaseEvent(self, event: QMouseEvent):
        """
        Reimplemented from QWidget::mouseReleaseEvent().
        param: event QMouseEvent
        """
        if event.button() == Qt.LeftButton:
            self.__m_LMPressed = False
        super().mouseReleaseEvent(event)

    def mouseMoveEvent(self, event: QMouseEvent):
        """
        Reimplemented from QWidget::mouseMoveEvent().
        param: event QMouseEvent
        """
        if self.__m_LMPressed:
            dockWidget = self.parentWidget()
            if dockWidget is not None:
                self.__m_LMPressed = False

                dockWidget.setFloating(True)

                event = QMouseEvent(QEvent.MouseButtonPress, 
                                    event.pos(), 
                                    self.mapToGlobal(event.pos()), 
                                    Qt.LeftButton, 
                                    Qt.LeftButton, 
                                    Qt.NoModifier)
                
                QApplication.postEvent(self, event)
        
        super().mouseMoveEvent(event)


class QEDockWidgetTabButton(QPushButton):
    def __init__(self,text, area: Qt.DockWidgetArea):
        super().__init__(text,None)
        self.__m_action = None
        self.__m_area = area
        self.__m_orientation = areaToOrientation(area)
        self.__m_mirrored = False
        
        
        self.setToolTip(text)

        fw = self.fontMetrics().width(text) + 12

        fw = 15 if fw < 15 else fw
        fw = 121 if fw > 120 else fw

        if self.__m_orientation == Qt.Vertical:
            self.setFixedSize(25, fw)
        elif self.__m_orientation == Qt.Horizontal:
            self.setFixedSize(fw, 25)
    
    def setAction(self, action):
        """
        Sets the action associated with the button.
        param: action QAction
        """
        self.__m_action = action
    
    def getAction(self):
        """
        Returns the action associated with the button.
        return: QAction
        """
        return self.__m_action
    
    def __setText(self,text):
        """
        Sets the text of the button.
        param: text str
        """
        aw = (self.width() -4) if self.__m_orientation == Qt.Horizontal else (self.height() -4)

        fm = self.fontMetrics()

        if aw < fm.width(text):
            m_text = ""

            for i in range(len(text)):
                m_text += text[i]

                if fm.width(m_text + ".......") > aw:
                    break
            
            self.setText(m_text + "...")
        else:
            self.setText(text)

    def __getStyleOption(self) -> QStyleOptionButton:
        """
        Get button style option
        :return: QStyleOptionButton
        """
        opt = QStyleOptionButton()
        opt.initFrom(self)

        if self.__m_orientation==Qt.Vertical:
            size = opt.rect.size()
            size.transpose()
            opt.rect.setSize(size)

        opt.features = QStyleOptionButton.None_

        if self.isFlat():
            opt.features |= QStyleOptionButton.Flat

        if self.menu():
            opt.features |= QStyleOptionButton.HasMenu

        if self.autoDefault or self.isDefault():
            opt.features |= QStyleOptionButton.AutoDefaultButton
        
        if self.isDefault():
            opt.features |= QStyleOptionButton.DefaultButton

        if self.isDown() or (self.menu() and self.menu().isVisible()):
            opt.state |= QStyle.State_Sunken
        
        if self.isChecked():
            opt.state |= QStyle.State_On

        if not self.isFlat() and not self.isDown():
           opt.state |= QStyle.State_Raised

        opt.text = self.text()
        opt.icon = self.icon()
        opt.iconSize = self.iconSize()
        
        return opt

    def sizeHint(self):
        """
        Returns the size hint for this widget.
        """
        size = super().sizeHint()
        if self.__m_orientation == Qt.Vertical:
            size.transpose()
        return size
    
    def paintEvent(self, event: QPaintEvent):
        """
        Args:
            event: Unused parameter, kept for compatibility with the API.
        """
        painter = QStylePainter(self)
        
        if self.__m_orientation == Qt.Vertical:
            if self.__m_mirrored:
                painter.rotate(-90)
                painter.translate(-self.height(), 0)
            else:
                painter.rotate(90)
                painter.translate(0, -self.width())
        
        painter.drawControl(QStyle.CE_PushButton, self.__getStyleOption())
        
    def resizeEvent(self, event):
        """
        Reimplemented from QWidget.resizeEvent.
        param event: QResizeEvent
        """
        self.__setText(self.text())

class DockWidgetState(Enum):
    Unknown = -1
    Docked = 0
    Floating = 1
    Hidden = 2
    Closed = 3


class QEDockWidget(QDockWidget):

    signal_pinned = pyqtSignal(object)
    signal_unpinned = pyqtSignal(object)
    signal_docked = pyqtSignal(object)
    signal_undocked = pyqtSignal(object)
    def __init__(self, title):
        super().__init__(title, None)
        self.__m_area = Qt.NoDockWidgetArea
        self.__m_state = DockWidgetState.Unknown
        self.__m_titleWidget = QEDockWidgetTitle()
        self.__m_layout = QVBoxLayout()
        self.__m_tabifieds = []
        self.__m_menuAction = None

        # self.signal_pinned = pyqtSignal(QEDockWidget)
        # self.signal_unpinned = pyqtSignal(QEDockWidget)
        # self.signal_docked = pyqtSignal(QEDockWidget)
        # self.signal_undocked = pyqtSignal(QEDockWidget)

        self.setObjectName("Dock Widget")

        self.setAutoFillBackground(True)

        self.setWindowTitle(title)

        self.setTitleBarWidget(self.__m_titleWidget)

        self.__m_layout.setContentsMargins(0, 2, 0, 0)

        widget = QWidget()
        widget.setLayout(self.__m_layout)
        super().setWidget(widget)

        self.__m_titleWidget.menuButton_pressed.connect(self.__openTitleMenu)
        self.__m_titleWidget.autoHideButton_pressed.connect(self.__autoHideStateToggle)
        self.__m_titleWidget.closeButton_pressed.connect(self.closeDockWidget)

        self.dockLocationChanged.connect(self.__updateDockLocation)
        self.topLevelChanged.connect(self.__updateTopLevelState)

    @pyqtSlot()
    def __slot_menuAction(self):
        """
        Menu action slot
        """
        pass
    def windowTitle(self):
        """
        Get window title
        @return: str
        """
        return self.__m_titleWidget.getText()
    
    def setWindowTitle(self, text):
        """
        Set window title
        @param text: str
        """
        title = "Noname" if text is None or len(text) == 0 else text
        self.__m_titleWidget.setText(title)
        super().setWindowTitle(title)

    def isMinimized(self):
        """
        Get window minimized state
        @return bool
        """
        return self.__m_state == DockWidgetState.Hidden
    
    def isDocked(self):
        """
        Get window docked state
        @return bool
        """
        return self.__m_state == DockWidgetState.Docked
    
    def isFloating(self):
        """
        Get window floating state
        @return bool
        """
        return self.__m_state == DockWidgetState.Floating
    
    def getArea(self):
        """
        Get window area
        @return QEDockWidgetArea
        """
        return self.__m_area
    
    def getState(self):
        """
        Get window state
        @return DockWidgetState
        """
        return self.__m_state
    
    def setState(self,state:DockWidgetState):
        """
        Set window state
        @param state
        """
        self.__m_state = state

        if state == DockWidgetState.Docked:
            self.__m_titleWidget.setFloating(True)
        elif state == DockWidgetState.Floating:
            self.__m_titleWidget.setFloating(False)
        elif state == DockWidgetState.Hidden:
            self.__m_titleWidget.setAutoHideEnadled(True)

    def closeDockWidget(self):
        """
        Closes the dock widget.
        """
        if self.isMinimized():
            self.signal_pinned.emit(self)

        self.setState(DockWidgetState.Closed)

        self.hide()

    def setWidget(self, widget):
        """
        Sets the widget of the dock widget.
        @param widget: QWidget
        """
        self.__m_layout.addWidget(widget)

    def setTabifiedDocks(self, dockWidgets):
        """
        Sets the tabified docks.
        @param dockWidgets: list of QEDockWidget
        """
        self.__m_tabifieds.clear()
        self.__m_tabifieds.extend(dockWidgets)

    def getTabifiedDocks(self):
        """
        Returns the tabified docks.
        @return: list of QEDockWidget
        """
        return self.__m_tabifieds
    
    def clearTabifiedDocks(self):
        """
        Clears the tabified docks.
        """
        self.__m_tabifieds.clear()

    def getMenuAction(self):
        """
        Returns the menu action.
        @return: QAction
        """
        return self.__m_menuAction
    
    def setMenuAction(self, action):
        """
        Sets the menu action.
        @param action: QAction
        """
        self.__m_menuAction = action

    def __openTitleMenu(self):
        """
        Opens the title menu.
        """
        menu = QMenu()

        action_float = QAction("Float", self)
        action_float.triggered.connect(self.__slot_menuAction)
        menu.addAction(action_float)

        action_dock = QAction("Dock", self)
        action_dock.triggered.connect(self.__slot_menuAction)
        menu.addAction(action_dock)

        action_AutoHide = QAction("Auto Hide", self)
        action_AutoHide.triggered.connect(self.__slot_menuAction)
        menu.addAction(action_AutoHide)
        
        action_Hide = QAction("Hide", self)
        action_Hide.triggered.connect(self.__slot_menuAction)
        menu.addAction(action_Hide)

        menu.exec(self.__m_titleWidget.menuPos())

    def __autoHideStateToggle(self):
        """
        Auto hide state toggle
        """
        if self.isMinimized():
            self.setState(DockWidgetState.Docked)
            self.signal_pinned.emit(self)
        else:
            self.setState(DockWidgetState.Hidden)
            self.signal_unpinned.emit(self)

    def __updateDockLocation(self,area : Qt.DockWidgetArea):
        """
        Update dock location
        @param area: Qt.DockWidgetArea
        """
        self.__m_area = area

        if self.__m_area != Qt.NoDockWidgetArea:
            self.__updateTopLevelState(False)

    def __updateTopLevelState(self,topLevel):
        """
        Update top level state
        @param topLevel: bool
        """
        self.__m_titleWidget.setAutoHideEnadled(False)

        if topLevel:
            self.setState(DockWidgetState.Floating)

            for item in self.__m_tabifieds:
                item.removeFromTabifiedDocks(self)
            
            self.clearTabifiedDocks()

            self.signal_undocked.emit(self)

    def removeFromTabifiedDocks(self,dockWidget):
        """
        Remove from tabified docks
        @param dockWidget: QEDockWidget
        """
        dock_widget_index = [i for i, widget in enumerate(self.__m_tabifieds) if widget == dockWidget]
        if dock_widget_index:
            self.__m_tabifieds.pop(dock_widget_index[0])

    def event(self,event : QEvent):
        """
        Event
        @param event: QEvent
        @return bool
        """
        if event.type() == QEvent.Enter:
            pass
        elif event.type() == QEvent.Leave:
            pass
        elif event.type() == QEvent.FocusOut:
            pass

        return super().event(event)
    
class QEDockWidgetTabBar(QToolBar):
    signal_dockWidgetButton_clicked = pyqtSignal(QEDockWidget)

    def __init__(self, area : Qt.DockWidgetArea):
        super().__init__()

        self.__m_area = area
        self.__m_spacer = None
        self.__m_tabs = dict()

        #self.signal_dockWidgetButton_clicked = pyqtSignal(QEDockWidget)

        self.setObjectName("QEDockWidgetTabBar")

        self.setFloatable(False)
        self.setMovable(False)

        self.setContextMenuPolicy(Qt.PreventContextMenu)

        self.setOrientation(areaToOrientation(area))

        self.layout().setSpacing(0)

        if self.orientation() == Qt.Horizontal:
            self.__m_spacer = QWidget()
            self.__m_spacer.setFixedWidth(0)
            self.addWidget(self.__m_spacer)
        
        self.hide()

    def addDockWidget(self,dockWidget : QEDockWidget):
        """
        Add a dock widget to the tab widget
        param dockWidget : QEDockWidget
        """
        if dockWidget is None: return
            
        dockWidgetTabButton = QEDockWidgetTabButton(dockWidget.windowTitle(),dockWidget.getArea())
        dockWidgetTabButton.clicked.connect(self.__dockWidgetButton_clicked)

        self.__m_tabs[dockWidgetTabButton] = dockWidget

        action = self.addWidget(dockWidgetTabButton)
        dockWidgetTabButton.setAction(action)

        if len(self.__m_tabs) == 1:
            self.show()
        
    def removeDockWidget(self, dockWidget : QEDockWidget):
        """
        Remove the given dock widget from the tab bar.
        param dockWidget: QEDockWidget
        return: bool
        """
        if dockWidget is None: return False
            
        try:
            dockWidgetTabButton, _ = next((button, widget) for button, widget in self.__m_tabs.items() if widget == dockWidget)
        except StopIteration:
            return False

        self.__m_tabs.pop(dockWidgetTabButton, None)
        self.removeAction(dockWidgetTabButton.getAction())

        if len(self.__m_tabs) == 0:
            self.hide()

        return True
    
    def insertSpacing(self):
        """
        Insert a spacer item
        """
        if self.__m_spacer is not None:
            self.__m_spacer.setFixedWidth(26)
    
    def removeSpacing(self):
        """
        Remove the spacer item
        """
        if self.__m_spacer is not None:
            self.__m_spacer.setFixedWidth(0)

    def __dockWidgetButton_clicked(self):
        """
        Dock widget button clicked
        """
        dockWidgetTabButton = self.sender()

        if dockWidgetTabButton is None: return
            
        try:
            _, dockWidget = next((button, widget) for button, widget in self.__m_tabs.items() if button == dockWidgetTabButton)
        except StopIteration:
            return
        
        self.signal_dockWidgetButton_clicked.emit(dockWidget)
        

class QEMainWindow(QMainWindow):
    def __init__(self,parent=None):
        super().__init__(parent)
        self.__m_menuWindows = QMenu(None)
        self.__m_dockWidget = QEDockWidget(None)
        self.__m_dockWidgets = []
        self.__m_dockWidgetBar = dict()

        super().addToolBarBreak()

        self.__createDockWidgetBar(Qt.LeftDockWidgetArea)
        self.__createDockWidgetBar(Qt.RightDockWidgetArea)
        self.__createDockWidgetBar(Qt.TopDockWidgetArea)
        self.__createDockWidgetBar(Qt.BottomDockWidgetArea)

    def addDockWidget(self,area :Qt.DockWidgetArea ,dockWidget: QEDockWidget):
        self.addDockWidgetO(area, dockWidget, Qt.Vertical)

    def addDockWidgetO(self,area :Qt.DockWidgetArea ,dockWidget: QEDockWidget,orientation:Qt.Orientation):
        if dockWidget is None: return
            
        dockWidget.signal_pinned.connect(self.__dockWidgetPinned)
        dockWidget.signal_unpinned.connect(self.__dockWidgetUnpinned)
        dockWidget.signal_docked.connect(self.__dockWidgetDocked)
        dockWidget.signal_undocked.connect(self.__dockWidgetUndocked)

        self.__m_dockWidgets.append(dockWidget)

        super().addDockWidget(area,dockWidget,orientation)

        if self.__m_menuWindows is None:
            self.__m_menuWindows = self.menuBar().addMenu("&Windows")
            self.__m_menuWindows.triggered.connect(self.__menuWindows_triggered)

        title = dockWidget.windowTitle()
        if len(title) == 0:
            title =  "Noname"

        action = QAction(title,self.__m_menuWindows)
        self.__m_menuWindows.addAction(action)

        dockWidget.setMenuAction(action)

    def removeDockWidget(self,dockWidget: QEDockWidget):
        if dockWidget is None: return
           
        if dockWidget not in self.__m_dockWidgets: return
           
        self.__m_dockWidgets.remove(dockWidget)

        if dockWidget.isMinimized():
            self.dockWidgetPinned(dockWidget)

        super().removeDockWidget(dockWidget)

        dockWidget.setParent(None)

        action = dockWidget.getMenuAction()
        if action is not None:
            self.__m_menuWindows.removeAction(action)

    def __hideDockWidget(self,dockWidget: QEDockWidget):

        if dockWidget is None or dockWidget.isHidden(): return

        self.__m_dockWidget = None
        dockWidget.hide()

    def __getDockWidgetsAreaRect(self):
        left = self.centralWidget().x()
        leftAreaDockWidgets = self.__getDockWidgetListAtArea(Qt.LeftDockWidgetArea)
        for dockwidget in leftAreaDockWidgets:
            if dockwidget.x() >= 0 and dockwidget.width() > 0:
                left = min(left,dockwidget.x())
        
        top = self.centralWidget().y()
        topAreaDockWidgets = self.__getDockWidgetListAtArea(Qt.TopDockWidgetArea)
        for dockwidget in topAreaDockWidgets:
            if dockwidget.y() >= 0 and dockwidget.height() > 0:
                top = min(top,dockwidget.y())

        right = self.centralWidget().x() + self.centralWidget().width()
        rightAreaDockWidgets = self.__getDockWidgetListAtArea(Qt.RightDockWidgetArea)
        for dockwidget in rightAreaDockWidgets:
            if dockwidget.x() >= 0 and dockwidget.width() > 0:
                right = max(right,dockwidget.x() + dockwidget.width())

        bottom = self.centralWidget().y() + self.centralWidget().height()
        bottomAreaDockWidgets = self.__getDockWidgetListAtArea(Qt.BottomDockWidgetArea)
        for dockwidget in bottomAreaDockWidgets:
            if dockwidget.y() >= 0 and dockwidget.height() > 0:
                bottom = max(bottom,dockwidget.y() + dockwidget.height())

        return QRect(left,top,right - left,bottom - top)
    
    def __adjustDockWidget(self,dockWidget: QEDockWidget):
        if dockWidget is None: return
           
        rect = self.__getDockWidgetsAreaRect()

        area = dockWidget.getArea()

        if area == Qt.LeftDockWidgetArea:
            dockWidget.setGeometry(rect.left(),rect.top(),dockWidget.width(),rect.height())
        elif area == Qt.TopDockWidgetArea:
            dockWidget.setGeometry(rect.left(),rect.top(),rect.width(),dockWidget.height())
        elif area == Qt.RightDockWidgetArea:
            dockWidget.setGeometry(rect.left() + dockWidget.width() - dockWidget.width(), rect.top(), dockWidget.width(), rect.height())
        elif area == Qt.BottomDockWidgetArea:
            dockWidget.setGeometry(rect.left(), rect.top() + rect.height() - dockWidget.height(), rect.width(), dockWidget.height())

    def __getDockWidgetBar(self,area: Qt.DockWidgetArea):
        #assert(area in self.__m_dockWidgetBar)
        if area in self.__m_dockWidgetBar:
            return self.__m_dockWidgetBar[area]
        return None
    
    def __getDockWidgetListAtArea(self,area: Qt.DockWidgetArea):
        dockWidgetList = []
        for dockWidget in self.__m_dockWidgets:
            if dockWidget.getArea() == area and dockWidget.isDocked():
                dockWidgetList.append(dockWidget)
        return dockWidgetList
    
    def __dockAreaToToolBarArea(self,area):
        if area == Qt.LeftDockWidgetArea:
            return Qt.LeftToolBarArea
        elif area == Qt.RightDockWidgetArea:
            return Qt.RightToolBarArea
        elif area == Qt.TopDockWidgetArea:
            return Qt.TopToolBarArea
        elif area == Qt.BottomDockWidgetArea:
            return Qt.BottomToolBarArea
        else:
            return Qt.ToolBarArea(0)
        
    def __createDockWidgetBar(self,area: Qt.DockWidgetArea):
        assert area not in self.__m_dockWidgetBar

        if area in self.__m_dockWidgetBar: return
            
        dockWidgetBar = QEDockWidgetTabBar(area)
        self.__m_dockWidgetBar[area] = dockWidgetBar
        dockWidgetBar.signal_dockWidgetButton_clicked.connect(self.__showDockWidget)

        self.addToolBar(self.__dockAreaToToolBarArea(area), dockWidgetBar)

    def __showDockWidget(self,dockWidget:QEDockWidget):
        if dockWidget is None: return
            
        if dockWidget.isHidden():

            self.__hideDockWidget(self.__m_dockWidget)

            if dockWidget.isFloating():
                super().addDockWidget(dockWidget.dockArea(),dockWidget)
                dockWidget.setFloating(False)

                super().removeDockWidget(dockWidget)

            self.__adjustDockWidget(dockWidget)

            dockWidget.show()
            dockWidget.raise_()
            dockWidget.setFocus()

            self.__m_dockWidget = dockWidget

        else:
            self.__hideDockWidget(dockWidget)

    def __dockWidgetPinned(self, dockWidget: QEDockWidget):
        if dockWidget is None: return
            
        dockWidgetBar = self.__getDockWidgetBar(dockWidget.getArea())

        if dockWidgetBar is None: return
            
        self.__m_dockWidget = None

        dockWidgetList = dockWidget.getTabifiedDocks()
        dockWidgetList.append(dockWidget)

        prevDockWidget = None

        for dockw in dockWidgetList:
            if dockWidgetBar.removeDockWidget(dockw):
                if prevDockWidget is None:
                    super().addDockWidget(dockWidget.getArea(),dockw)
                else:
                    self.tabifyDockWidget(prevDockWidget, dockw)

                
                prevDockWidget = dockw

                dockw.setState(DockWidgetState.Docked)

                dockw.show()

        dockWidget.raise_()

        if dockWidget.getArea() == Qt.LeftDockWidgetArea and dockWidgetBar.isHidden():
            self.__getDockWidgetBar(Qt.TopDockWidgetArea).removeSpacing()
            self.__getDockWidgetBar(Qt.BottomDockWidgetArea).removeSpacing()

    def __dockWidgetUnpinned(self,dockWidget:QEDockWidget):
        if dockWidget is None: return
           
        dockWidgetBar = self.__getDockWidgetBar(dockWidget.getArea())

        if dockWidgetBar is None: return
        
        dockWidgetList = self.tabifiedDockWidgets(dockWidget)
        dockWidgetList.append(dockWidget)

        for dockW in dockWidgetList:
            dockW.setState(DockWidgetState.Hidden)

            if not dockW.isHidden():
                dockWidgetBar.addDockWidget(dockW)

                dockW.setTabifiedDocks(dockWidgetList)

                super().removeDockWidget(dockW)

        if dockWidget.getArea() == Qt.LeftDockWidgetArea:
            self.__getDockWidgetBar(Qt.TopDockWidgetArea).insertSpacing()
            self.__getDockWidgetBar(Qt.BottomDockWidgetArea).insertSpacing()

    def __dockWidgetDocked(self,dockWidget:QEDockWidget):
        if dockWidget is None: return
           
    
    def __dockWidgetUndocked(self,dockWidget:QEDockWidget):
        self.__hideDockWidget(self.__m_dockWidget)

        dockWidgetBar = self.__getDockWidgetBar(dockWidget.getArea())

        if dockWidgetBar is None: return
           
        dockWidget.clearTabifiedDocks()

        if dockWidgetBar.removeDockWidget(dockWidget):
            if not dockWidget.isFloating():
                super().addDockWidget(dockWidget.getArea(),dockWidget)

            if dockWidget.getArea() == Qt.LeftDockWidgetArea and dockWidgetBar.isHidden():
                self.__getDockWidgetBar(Qt.TopDockWidgetArea).removeSpacing()
                self.__getDockWidgetBar(Qt.BottomDockWidgetArea).removeSpacing()
            
            dockWidget.show()

    def __menuWindows_triggered(self,action:QAction):
        
        a_widget = None
        for dockwidget in self.__m_dockWidgets:
            if action == dockwidget.getMenuAction():
                a_widget = dockwidget
        
        if a_widget is None: return

        if a_widget.isHidden():
            self.__hideDockWidget(self.__m_dockWidget)

            a_widget.show()
            a_widget.raise_()
        elif a_widget.isMinimized():
            self.__showDockWidget(a_widget)

        a_widget.setFocus()
